/*
 *    Copyright © OpenAtom Foundation.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package com.inspur.edp.cef.spi.jsonser.abstractcefchange;

import static com.fasterxml.jackson.core.JsonToken.FIELD_NAME;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.inspur.edp.cef.api.RefObject;
import com.inspur.edp.cef.api.manager.serialize.CefSerializeContext;
import com.inspur.edp.cef.api.manager.serialize.JsonFormatType;
import com.inspur.edp.cef.entity.changeset.AbstractModifyChangeDetail;
import com.inspur.edp.cef.entity.changeset.AddChangeDetail;
import com.inspur.edp.cef.entity.changeset.AddOrModifyChangeDetail;
import com.inspur.edp.cef.entity.changeset.ChangeType;
import com.inspur.edp.cef.entity.changeset.DeleteChangeDetail;
import com.inspur.edp.cef.entity.changeset.IChangeDetail;
import com.inspur.edp.cef.entity.changeset.InnerUtil;
import com.inspur.edp.cef.entity.changeset.ModifyChangeDetail;
import com.inspur.edp.cef.entity.config.SerializerUtils;
import com.inspur.edp.cef.entity.entity.IEntityData;
import com.inspur.edp.cef.entity.i18n.MultiLanguageInfo;
import com.inspur.edp.cef.spi.extend.IDataExtendSerializerItem;
import com.inspur.edp.cef.spi.extend.entity.ICefEntityExtend;
import com.inspur.edp.cef.spi.jsonser.entity.AbstractEntitySerializerItem;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
import org.springframework.util.StringUtils;

public abstract class AbstractCefChangeJsonDeserializer<T extends com.inspur.edp.cef.spi.jsonser.abstractcefchange.AbstractCefDataSerItem> extends JsonDeserializer<IChangeDetail> {
    public AbstractCefChangeJsonDeserializer(List<T> deSerializers)
    {
        this.deSerializers=deSerializers;
    }
    protected java.util.List<T> deSerializers;
    protected CefSerializeContext context;
    private java.util.List<T> serItems;

    protected java.util.List<T> getSerializerItems() {
        if (serItems == null) {
            List items = new ArrayList(deSerializers);
            if (extendList != null) {
                extendList.stream().map(item -> item.getSerializerItem())
                    .filter(item -> item != null).forEach(items::add);
            }
            serItems = items;
        }
        return serItems;
    }

    @Override
    public IChangeDetail deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        return readJson(p,ctxt);
    }

    public final IChangeDetail readJson(JsonParser p, DeserializationContext ctxt)throws IOException, JsonProcessingException
    {
        if(p.getCurrentToken()!= JsonToken.START_OBJECT)
        {
            throw new RuntimeException("json结构异常");
        }
        JsonNode node = null;
        //todo p.readValueAsTree()后，所有token都变成null了
//        if(context.getJsonFormatType() == JsonFormatType.Tiled)
//            node = p.readValueAsTree();
        p.nextToken();
        p.nextToken();

        String changeType =p.getValueAsString();
        IChangeDetail detail = createChangeDetail(p, changeType, node, ctxt);
        p.getCurrentToken();
        p.nextToken();
        return detail;
    }

    private IChangeDetail createChangeDetail(JsonParser p, String changeType, JsonNode node, DeserializationContext ctxt)throws IOException, JsonProcessingException {
        IChangeDetail detail = null;
        if (! ChangeType.isStringChangeType(changeType)) {
            throw new RuntimeException("输入的变更集类型不正确："+changeType);
        }
        ChangeType type = ChangeType.valueOf(changeType);
        p.getCurrentToken();
        p.getCurrentToken();
        p.nextToken();
        p.nextToken();
        switch (type)
        {
            case Added:
                detail = createAddChangeDetail(p, ctxt, (IEntityData) createData());
                break;
            case Deleted:
                detail = createDeleteChangeDetail(p, ctxt);
                break;
            case Modify:
                detail = innerCreateModifyChangeDetail(p, ctxt, node == null ? null : node.findValue("ChangeInfo"));
                break;
            case AddedOrModify:
                detail = innerCreateAddedOrModifyChangeDetail(p, ctxt, node == null ? null : node.findValue("ChangeInfo"));
                break;
            default:
                throw new RuntimeException("");
        }

        return detail;
    }

    protected IChangeDetail innerCreateAddedOrModifyChangeDetail(JsonParser p, DeserializationContext ctxt, JsonNode node)
        throws IOException {
        ModifyChangeDetail modifyChangeDetail = (ModifyChangeDetail)innerCreateModifyChangeDetail(p, ctxt, node);
        return new AddOrModifyChangeDetail(modifyChangeDetail);
    }

    protected abstract IEntityData createData();

    protected AbstractCefDataDeSerializer getCefDataDeSerializer()
    {
        return null;
    }

    protected AbstractCefDataDeSerializer createDataDeSerializer(){
        return getCefDataDeSerializer();
    }

    private AddChangeDetail createAddChangeDetail(JsonParser p, DeserializationContext ctxt, IEntityData data)
            throws IOException, JsonProcessingException
    {
        AbstractCefDataDeSerializer dataDeSerializer = createDataDeSerializer();
        if(dataDeSerializer!= null)
        {
            if(getExtendList() != null){
                for(ICefEntityExtend extender : getExtendList()){
                    dataDeSerializer.addExtend(extender);
                }
                dataDeSerializer.setConfigId(configId);
            }
            data = (IEntityData)dataDeSerializer.readJson(p,ctxt);
        }
        else
        {
            throw new UnsupportedOperationException();
            //Java版临时屏蔽
//            serializer.Populate(reader, data);
        }
        AddChangeDetail detail = new AddChangeDetail(data);
        return detail;
    }

    private DeleteChangeDetail createDeleteChangeDetail(JsonParser p, DeserializationContext ctxt)
            throws IOException, JsonProcessingException
    {
        p.getCurrentToken();
        p.nextToken();
        String name =p.getValueAsString();
        p.nextToken();
        String dataId = p.getValueAsString();
        p.nextToken();
        return new DeleteChangeDetail(dataId);
    }

    private AbstractModifyChangeDetail innerCreateModifyChangeDetail(JsonParser p, DeserializationContext ctxt, JsonNode node)
            throws IOException, JsonProcessingException
    {
        p.nextToken();
        return readModifyChangeDetail(p, ctxt, node);
    }

    public final AbstractModifyChangeDetail readModifyChangeDetail(JsonParser p, DeserializationContext ctxt, JsonNode node)
            throws IOException, JsonProcessingException {

        AbstractModifyChangeDetail detail = createModifyChangeDetail(p,ctxt);
        HashMap<String, Object> changeValues = new HashMap<>();
        while (p.getCurrentToken()== JsonToken.FIELD_NAME)
        {
            String propertyName =p.getValueAsString();
            p.nextToken();
            if (readModifyChangeMultiLanguageInfo(p, detail, propertyName)) {
                continue;
            }
            readModifyChangeDetailProperty(p,ctxt, detail, propertyName, changeValues);
            p.nextToken();
        }
        if (detail.getMultiLanguageInfos().size() > 0) {
            InnerUtil.mergeMultiLanguageInfoToChanges(detail.getMultiLanguageInfos(), null,
                detail.getPropertyChanges());
        }
        return detail;
    }

    //TODO: 挪到MultiLangSerializationUtil
    private boolean readModifyChangeMultiLanguageInfo(JsonParser p,
        AbstractModifyChangeDetail detail, String propertyName) {
        boolean hasRead = false;
        if (propertyName != null && propertyName.endsWith(MultiLanguageInfo.MULTILANGUAGETOKEN)) {
            String webPropertyName = propertyName.split(MultiLanguageInfo.MULTILANGUAGETOKEN)[0];
            String bffPropertyName = "";
            for (AbstractCefDataSerItem item : getSerializerItems()) {
                if (item instanceof AbstractEntitySerializerItem) {
                    AbstractEntitySerializerItem entitySerializerItem = (AbstractEntitySerializerItem) item;
                    Map<String, String> propertyNameMap = entitySerializerItem
                        .getTransferPropertyNameMap();
                    if (propertyNameMap == null)
                        continue;
                    if (propertyNameMap.containsKey(webPropertyName)) {
                        bffPropertyName = entitySerializerItem.getTransferPropertyNameMap()
                            .get(webPropertyName) + MultiLanguageInfo.MULTILANGUAGETOKEN;
                        break;
                    } else {
                        for (Map.Entry<String, String> mapItem:propertyNameMap.entrySet())
                        {
                            if(mapItem.getKey().equalsIgnoreCase(webPropertyName))
                                bffPropertyName = mapItem.getValue() + MultiLanguageInfo.MULTILANGUAGETOKEN;
                        }
                    }
                }
            }
            if (bffPropertyName.isEmpty()) {
                throw new RuntimeException("变更集中的多语属性名无法识别：" + propertyName);
            }
            detail.getMultiLanguageInfos()
                .put(bffPropertyName, readMultiLanguageInfo(p, bffPropertyName));
            hasRead = true;
        }
        return hasRead;
    }

    private MultiLanguageInfo readMultiLanguageInfo(JsonParser parser, String propName) {
        MultiLanguageInfo info = new MultiLanguageInfo();
        info.setPropName(propName);
        try {
            SerializerUtils.readStartObject(parser);
            while (parser.getCurrentToken() == FIELD_NAME) {
                String language = SerializerUtils.readPropertyName(parser);
                String value = SerializerUtils.readPropertyValue_String(parser);
                info.getPropValueMap().put(language, value);
            }
            SerializerUtils.readEndObject(parser);
            return info;
        } catch (RuntimeException e) {
            throw new RuntimeException("反序列化MultiLanguageInfo类型失败", e);
        }
    }

    protected abstract AbstractModifyChangeDetail createModifyChangeDetail(JsonParser p, DeserializationContext ctxt) throws IOException;

//    private HashMap<String, Object> changeValues = new HashMap<>();

    protected void readModifyChangeDetailProperty(JsonParser p, DeserializationContext ctxt, AbstractModifyChangeDetail changeDetail, String propertyName, HashMap<String, Object> changeValues) throws IOException {
        boolean hasRead = false;
//        HashMap<String, Object> changeValues = new HashMap<>();
        for (AbstractCefDataSerItem item : getSerializerItems()){

            RefObject<String> tempRef_propertyName = new RefObject<String>(propertyName);
            RefObject<Boolean> tempRef_hasRead = new RefObject<Boolean>(hasRead);
            if(item instanceof IDataExtendSerializerItem && !StringUtils.isEmpty(configId)){
                ((IDataExtendSerializerItem)item).setConfigId(configId);
            }
            Object transValue = null;
            if(context != null && context.getJsonFormatType() == JsonFormatType.Tiled){

                transValue = item.readModifyPropertyValue(p, ctxt, changeValues, tempRef_propertyName, tempRef_hasRead, context);
            }else{
                transValue = item.readModifyPropertyValue(p,ctxt,tempRef_propertyName,tempRef_hasRead);
            }

            propertyName = tempRef_propertyName.argvalue;
            hasRead = tempRef_hasRead.argvalue;
            if (hasRead) {
                changeDetail.getPropertyChanges().put(propertyName, transValue);
                break;
            }
        }

        if (!ignoreUnrecognizedProp && !hasRead) {
            StringBuilder stringBuilder=new StringBuilder();
            stringBuilder.append(String.format("未识别的变更名称：%1$s,", propertyName));
            stringBuilder.append("对应的类：");
            for (AbstractCefDataSerItem item : getSerializerItems()) {
                stringBuilder.append(item.getClass().getTypeName()).append("，");
            }
            throw new RuntimeException(stringBuilder.toString());
        }
    }

    public void setIgnoreUnrecognizedProp(boolean ignoreUnrecognizedProp) {
        this.ignoreUnrecognizedProp = ignoreUnrecognizedProp;
    }

    protected boolean isIgnoreUnrecognizedProp() {
        return ignoreUnrecognizedProp;
    }

    private boolean ignoreUnrecognizedProp = false;

    private String configId;
    @Deprecated
    public void setConfigId(String value){
        configId = value;
    }

    private List<ICefEntityExtend> extendList;

    public void addExtend(ICefEntityExtend extList){
        Objects.requireNonNull(extList);

        if(extendList == null){
            extendList = new ArrayList<>();
        }
        extendList.add(extList);
    }

    protected List<ICefEntityExtend> getExtendList(){
        return extendList == null ? Collections.emptyList() : extendList;
    }

    
    public void setCefSerializeContext(CefSerializeContext context) {
        this.context = context;
      if (this.deSerializers != null && this.deSerializers.size() > 0) {
        for (T item : deSerializers
        ) {
          item.setCefSerializeContext(context);
        }
      }
    }
}
